#ifndef CARDSET_H
#define CARDSET_H

#include "cardtypes.h"
#include "cardtools.h"
#include "combinations.h"
#include <algorithm>

/*****************************************************************************/
/**                               class CCardSet                            **/
/*****************************************************************************/

class CCardSet
{
public:
    typedef enum {stValue, stSuit} SortType;

    CCardSet(size_t capacity = 0)
    {
        mCards.reserve(capacity);
    }

    virtual ~CCardSet() {}

    void addCard(const CCard& card)
    {
        mCards.push_back(card);
    }

    void removeCard(const CCard& card)
    {
        for (size_t i = 0; i < mCards.size(); i++ )
            if ( mCards[i] == card )
            {
                mCards.erase(mCards.begin() + i);
                break;
            }
    }

    void sortCards(SortType st);

    size_t size() const { return mCards.size(); }

    string getSignature() const;

    vector<CCard>::iterator begin() { return mCards.begin(); }
    vector<CCard>::iterator end() { return mCards.end(); }
    vector<CCard>::const_iterator begin() const { return mCards.cbegin(); }
    vector<CCard>::const_iterator end() const { return mCards.cend(); }

protected:
    vector<CCard> mCards;
};

ostream& operator << (ostream& ostr, const CCardSet& cardSet);

/*****************************************************************************/
/**                                class CDeck                              **/
/*****************************************************************************/

class CDeck: public CCardSet
{
public:
    CDeck(bool createEmpty = false):CCardSet(52)
    {
        if (! createEmpty )
            for (int s = CCard::sHearts; s <= CCard::sSpades; s++)
                for(int v = CCard::vTwo; v <= CCard::vAce; v++)
                    addCard( CCard(CCard::SuitType(s), CCard::ValueType(v)) );
    };
};

/*****************************************************************************/
/**                                class CHand                              **/
/*****************************************************************************/

class CHand: public CCardSet
{
public:
    template<class Iter>
    CHand(Iter it):CCardSet(5)
    {
        for (int i = 0; i < 5; i++, it++)
            addCard(*it);
        calcStrength();
    }

    CHand(initializer_list<string> sCards):CCardSet(5)
    {
        for (const string& sCard: sCards)
            addCard( CCard(sCard) );
        calcStrength();
    }

    HANDSTENGTH getStrength() const  { return mStrength; }
    COMBINATION_TYPE getCombination() const { return mCombination; }
private:
    void calcStrength()
    {
        if ( mCards.size() != 5)
            throw "CHand::calcStrength(): Incompleate hand";
        sortCards(CCardSet::stSuit);

        mCombination = mCombinationCalc.calcCombinationType(*this);
        mStrength = mStrenghtCalc.getStrength(mCombination, getSignature());
    }
    static CCardStrengthCalculator mStrenghtCalc;
    static CCombination mCombinationCalc;
    HANDSTENGTH mStrength;
    COMBINATION_TYPE mCombination;

    friend bool operator < (const CHand& lhs, const CHand& rhs  );
    friend bool operator > (const CHand& lhs, const CHand& rhs  );
    friend bool operator == (const CHand& lhs, const CHand& rhs  );
};

inline bool operator > (const CHand& lhs, const CHand& rhs  )
{
    return lhs.mStrength > rhs.mStrength;
}

inline bool operator < (const CHand& lhs, const CHand& rhs  )
{
    return lhs.mStrength < rhs.mStrength;
}

inline bool operator == (const CHand& lhs, const CHand& rhs  )
{
    return lhs.mStrength == rhs.mStrength;
}

ostream& operator << (ostream& ostr, const CHand& hand);

#endif // CARDSET_H
